
# ------------------------------------------------------------------------------------------
# # 了解Julia
#
#
# 这个notebook用于速成Julia语法，让你了解Julia是怎么着轻量级、怎么着好用——就像你最喜欢的高级语言一样！
#
# 将涉及：
# - Strings 字符串
# - Data structures 数据结构
# - Loops 循环
# - Conditionals 条件判断
# - Functions 函数
# ------------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------------
# ## Strings 字符串
# ------------------------------------------------------------------------------------------

string1 = "How many cats "

string2 = "is too many cats?"

string(string1, string2)

😺 = 10
println("I don't know but $😺 are too few!")

# ------------------------------------------------------------------------------------------
# 注意：Julia允许用超广泛的字符，就比如😺
#
# 这就让我们可以把代码写成这个样子
# ------------------------------------------------------------------------------------------

😺 = 1
😀 = 0
😞 = -1

😺 + 😞 == 😀

# ------------------------------------------------------------------------------------------
# ## Data structures 数据结构
# ------------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------------
# ### Tuples 元组
#
# 要创建创建一个元组，用小括号`( )`括起一系列顺序的元素
#
#
# 语法： <br>
# ```julia
# (item1, item2, ...)```
# ------------------------------------------------------------------------------------------

myfavoriteanimals = ("penguins", "cats", "sugargliders")

myfavoriteanimals[1]

# ------------------------------------------------------------------------------------------
# ### Dictionaries 字典
#
# 想存键值对，用字典。通过`Dict()`函数来创建字典。
#
# 语法：
# ```julia
# Dict(key1 => value1, key2 => value2, ...)```
#
# 比如存电话簿，人名和电话号码成键值对关系
# ------------------------------------------------------------------------------------------

myphonebook = Dict("Jenny" => "867-5309", "Ghostbusters" => "555-2368")

myphonebook["Jenny"]

# ------------------------------------------------------------------------------------------
# ### Arrays 数组
#
# 不同于元组，数组可变；不同于字典，数组元素有顺序。 <br>
# 要创建创建一个数组，用方括号`[ ]`括起一系列顺序的元素。
#
# Syntax: <br>
# ```julia
# [item1, item2, ...]```
#
# 像这样：
# ------------------------------------------------------------------------------------------

myfriends = ["Ted", "Robyn", "Barney", "Lily", "Marshall"]

fibonacci = [1, 1, 2, 3, 5, 8, 13]

mixture = [1, 1, 2, 3, "Ted", "Robyn"]

# ------------------------------------------------------------------------------------------
# 数组可以包含其他数据结构，也可以包含数据变成高维数组
# ------------------------------------------------------------------------------------------

numbers = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]

rand(4, 3)

# ------------------------------------------------------------------------------------------
# ## Loops 循环
#
# ### `for`循环
#
# `for`循环语法：
#
# ```julia
# for *var* in *loop iterable*
#     *loop body*
# end
# ```
# ------------------------------------------------------------------------------------------

for n in 1:10
    println(n)
end

# ------------------------------------------------------------------------------------------
# ### `while`循环
#
# `while`循环语法：
#
# ```julia
# while *condition*
#     *loop body*
# end
# ```
# ------------------------------------------------------------------------------------------

n = 0
while n < 10
    n += 1
    println(n)
end

# ------------------------------------------------------------------------------------------
# ## Conditionals 条件判断
#
# #### `if`语法
#
# Julia中if语法为：
#
# ```julia
# if *condition 1*
#     *option 1*
# elseif *condition 2*
#     *option 2*
# else
#     *option 3*
# end
# ```
#
# 这让我们可以按条件执行不同的语句
# ------------------------------------------------------------------------------------------

x, y = 1, 2
if x > y
    x
else
    y
end

# ------------------------------------------------------------------------------------------
# #### 三元运算符
#
# 我们可以用三元运算符语法重写上一个cell
#
# ```julia
# a ? b : c
# ```
#
# 它等价于
#
# ```julia
# if a
#     b
# else
#     c
# end
# ```
# ------------------------------------------------------------------------------------------

(x > y) ? x : y

# ------------------------------------------------------------------------------------------
# ## Functions 函数
#
# 话题：
# 1. 如何声明一个函数
# 2. Julia中的鸭子类型（Duck-typing）
# 3. 原地修改与非原地修改函数 Mutating vs. non-mutating functions
# 4. 高阶函数
#
# ### 如何声明一个函数
#
# #### 方法一：用`function` 和 `end` 关键字
# ------------------------------------------------------------------------------------------

function f(x)
    x^2
end

# ------------------------------------------------------------------------------------------
# #### 方法二：用`=`
# ------------------------------------------------------------------------------------------

f2(x) = x^2

# ------------------------------------------------------------------------------------------
# 方法三：匿名函数
# ------------------------------------------------------------------------------------------

f3 = x -> x^2

# ------------------------------------------------------------------------------------------
# #### 调用函数
# ------------------------------------------------------------------------------------------

f(42)

f2(42)

f3(42)

# ------------------------------------------------------------------------------------------
# ### Julia中的鸭子类型（Duck-typing）
# *“假如它嘎嘎像个鸭鸭，那它就是个鸭鸭”* <br><br>
# 不管输入参数是什么类型，只要能用，那么Julia函数就能使 <br><br>
# 比如上面的`f`函数可以接受一个矩阵（matrix）作为参数
# ------------------------------------------------------------------------------------------

A = rand(3, 3)

f(A)

# ------------------------------------------------------------------------------------------
# 然而，`f`函数无法接受一个向量（vector）。不同于`A^2`是已经定义好的矩阵乘方，当`v`是向量时的`v^2`是有歧义的。
# ------------------------------------------------------------------------------------------

v = rand(3)

f(v)

# ------------------------------------------------------------------------------------------
# ### 原地修改与非原地修改函数 Mutating vs. non-mutating functions
#
# 约定：函数名以`!`结束的原地修改传入的变量，函数名非以`!`结束的则不会改变传入的变量。
#
# 例如下面的`sort`和`sort!`
# ------------------------------------------------------------------------------------------

v = [3, 5, 2]

sort(v)

v

# ------------------------------------------------------------------------------------------
# `sort(v)`会返回一个新的、将`v`排序后的数组，`v`本身不会改变 <br><br>
#
# 而当执行`sort!(v)`后，`v`将会被原地排序修改
# ------------------------------------------------------------------------------------------

sort!(v)

v



# ------------------------------------------------------------------------------------------
# ### 一些高阶函数
#
# #### map
#
# `map`是Julia中的高阶函数。“高阶”指的是它会*接受一个函数*作为它的传入参数。
# `map`将传入的函数作用于传入的数据结构的每一个元素。例如执行
#
# ```julia
# map(f, [1, 2, 3])
# ```
# 将得到一个输出数组，数组的每一个元素为`f`作用在数组`[1, 2, 3]`的对应元素
# ```julia
# [f(1), f(2), f(3)]
# ```
# ------------------------------------------------------------------------------------------

map(f, [1, 2, 3])

# ------------------------------------------------------------------------------------------
# 这里我们把向量`[1, 2, 3]`的每一个元素都平方了，而不是对向量`[1, 2, 3]`本身进行平方。
#
# `map`可以接受匿名函数作为参数，像这样
# ------------------------------------------------------------------------------------------

map(x -> x^3, [1, 2, 3])

# ------------------------------------------------------------------------------------------
# 这就完成了对数组`[1, 2, 3]`所有元素的三次方计算！
# ------------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------------
# ### broadcast
#
# `broadcast`和`map`一样也是个高阶函数。`broadcast`一种广义化的`map`,所以`map`能做的它都能做，并不止如此。`broadcast`的调用方法和`m
# ap`一样。
# ------------------------------------------------------------------------------------------

broadcast(f, [1, 2, 3])

# ------------------------------------------------------------------------------------------
# 我们又一次通过函数`f`把向量`[1, 2, 3]`的每一个元素都平方了——这次是用“广播”函数`f`的方式！
#
# `broadcast`有个语法糖：调用要广播的函数时在函数名和输入变量之间加一个`.`。比如：
#
# ```julia
# broadcast(f, [1, 2, 3])
# ```
# 就等价于
# ```julia
# f.([1, 2, 3])
# ```
# ------------------------------------------------------------------------------------------

f.([1, 2, 3])

# ------------------------------------------------------------------------------------------
# 再一次强调`map`和`broadcast`方式与如下直接调用函数的区别
# ```julia
# f([1, 2, 3])
# ```
# 我们可以求向量中每个元素的平方，但是不能求向量的平方！
# ------------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------------
# 为了再讲清楚这一点，我们再看一下以下调用的区别
#
# ```julia
# f(A)
# ```
# 与
# ```julia
# f.(A)
# ```
# 其中`A`是个矩阵：
# ------------------------------------------------------------------------------------------

A = [i + 3*j for j in 0:2, i in 1:3]

f(A)

# ------------------------------------------------------------------------------------------
# 和之前一样，对于矩阵`A`来说
# ```
# f(A) = A^2 = A * A
# ```
#
# 另一方面，
# ------------------------------------------------------------------------------------------

B = f.(A)

# ------------------------------------------------------------------------------------------
# 包含了矩阵`A`中每个元素的平方
#
# 广播的点语法让比较复杂的元素间运算的表示更自然、更贴近数学表示，比如：
# ------------------------------------------------------------------------------------------

C = A .+ 2 .* f.(A) ./ A

# ------------------------------------------------------------------------------------------
# 而不是
# ------------------------------------------------------------------------------------------

broadcast(x -> x + 2 * f(x) / x, A)

# ------------------------------------------------------------------------------------------
# 并且这么写仍然能编译到和C语言一样高效的代码！
# ------------------------------------------------------------------------------------------
